using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Forms;
using LibNDSFormats.NSBMD;
using Tao.OpenGl;
using LibNDSFormats;

namespace MKDS_Course_Editor
{
    public class HBDF
    {
        public class MDLF
        {
            private const float SCALE_IV = 4096.0f;
            private static int stackID;
            private static bool g_mat = true;
            private static string file;
            private static bool gOptColoring = true;
            private static bool gOptTexture = true;
            private static int gCurrentVertex;
            private static bool gOptVertexMode;
            private static MTX44 CurrentMatrix;
            private static MTX44[] MatrixStack = new MTX44[31];
            /// <summary>
        /// Process polygon 3d commands.
        /// </summary>
        /// <param name="polydata">Data of specific polygon.</param>
            private static void Process3DCommand(byte[] polydata)
            {
                if (polydata == null)
                    return;
                int commandptr = 0;
                int commandlimit = polydata.Length;
                int[] command = new int[4];
                int cur_vertex, mode, i;
                float[] vtx_state = { 0.0f, 0.0f, 0.0f };
                float[] vtx_trans = { 0.0f, 0.0f, 0.0f };

                cur_vertex = gCurrentVertex; // for vertex_mode
                CurrentMatrix = MatrixStack[stackID].Clone();
                while (commandptr < commandlimit)
                {
                    for (i = 0; i < 4; ++i)
                    {
                        if (commandptr >= commandlimit)
                            command[i] = 0xFF;
                        else
                        {
                            command[i] = polydata[commandptr];
                            commandptr++;
                        }
                    }


                    for (i = 0; i < 4 && commandptr < commandlimit; i++)
                    {
                        switch (command[i])
                        {
                            case 0: // No Operation (for padding packed GXFIFO commands)
                                break;
                            case 0x14:
                                /*
                                      MTX_RESTORE - Restore Current Matrix from Stack (W)
                                      Sets C=[N]. The stack pointer S is not used, and is left unchanged.
                                      Parameter Bit0-4:  Stack Address (0..30) (31 causes overflow in GXSTAT.15)
                                      Parameter Bit5-31: Not used
                                    */

                                stackID = Utils.Read4BytesAsInt32(polydata, commandptr) & 0x0000001F;
                                commandptr += 4;
                                CurrentMatrix = MatrixStack[stackID].Clone();

                                break;

                            case 0x1b:
                                /*
                                      MTX_SCALE - Multiply Current Matrix by Scale Matrix (W)
                                      Sets C=M*C. Parameters: 3, m[0..2] (MTX_SCALE doesn't change Vector Matrix)
                                    */
                                {
                                    int x, y, z;
                                    x = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;
                                    y = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;
                                    z = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;
                                    CurrentMatrix.Scale(x / SCALE_IV, y / SCALE_IV, z / SCALE_IV);
                                    break;
                                }
                            case 0x20: // Directly Set Vertex Color (W)
                                {
                                    int rgb, r, g, b;

                                    rgb = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    if (gOptColoring)
                                    {
                                        r = (rgb >> 0) & 0x1F;
                                        g = (rgb >> 5) & 0x1F;
                                        b = (rgb >> 10) & 0x1F;
                                        Gl.glColor3f(((float)r) / 31.0f, ((float)g) / 31.0f, ((float)b) / 31.0f);
                                    }
                                }
                                break;

                            case 0x21:
                                /*
                                      Set Normal Vector (W)
                                      0-9   X-Component of Normal Vector (1bit sign + 9bit fractional part)
                                      10-19 Y-Component of Normal Vector (1bit sign + 9bit fractional part)
                                      20-29 Z-Component of Normal Vector (1bit sign + 9bit fractional part)
                                      30-31 Not used
                                    */
                                {
                                    int xyz, x, y, z;

                                    xyz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    x = (xyz >> 0) & 0x3FF;
                                    if ((x & 0x200) != 0)
                                        x |= -1024;
                                    y = (xyz >> 10) & 0x3FF;
                                    if ((y & 0x200) != 0) y |= -1024;
                                    z = (xyz >> 20) & 0x3FF;
                                    if ((z & 0x200) != 0) z |= -1024;
                                    Gl.glNormal3f(((float)x) / 512.0f, ((float)y) / 512.0f, ((float)z) / 512.0f);
                                    break;
                                }
                            case 0x22:
                                /*
                                      Set Texture Coordinates (W)
                                      Parameter 1, Bit 0-15   S-Coordinate (X-Coordinate in Texture Source)
                                      Parameter 1, Bit 16-31  T-Coordinate (Y-Coordinate in Texture Source)
                                      Both values are 1bit sign + 11bit integer + 4bit fractional part.
                                      A value of 1.0 (=1 SHL 4) equals to one Texel.
                                    */
                                {
                                    int st, s, t;

                                    st = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    s = (st >> 0) & 0xffff;
                                    if ((s & 0x8000) != 0) s |= -65536;
                                    t = (st >> 16) & 0xffff;
                                    if ((t & 0x8000) != 0) t |= -65536;
                                    Gl.glTexCoord2f(((float)s) / 16.0f, ((float)t) / 16.0f);
                                    break;
                                }
                            case 0x23:
                                /*
                                      VTX_16 - Set Vertex XYZ Coordinates (W)
                                      Parameter 1, Bit 0-15   X-Coordinate (signed, with 12bit fractional part)
                                      Parameter 1, Bit 16-31  Y-Coordinate (signed, with 12bit fractional part)
                                      Parameter 2, Bit 0-15   Z-Coordinate (signed, with 12bit fractional part)
                                      Parameter 2, Bit 16-31  Not used
                                    */
                                {
                                    int parameter, x, y, z;

                                    parameter = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    x = (parameter >> 0) & 0xFFFF;
                                    if ((x & 0x8000) != 0) x |= -65536;
                                    y = (parameter >> 16) & 0xFFFF;
                                    if ((y & 0x8000) != 0) y |= -65536;

                                    parameter = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;
                                    z = parameter & 0xFFFF;
                                    if ((z & 0x8000) != 0) z |= -65536;

                                    vtx_state[0] = ((float)x) / SCALE_IV;
                                    vtx_state[1] = ((float)y) / SCALE_IV;
                                    vtx_state[2] = ((float)z) / SCALE_IV;
                                    if (stackID != -1)
                                    {
                                        vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                        Gl.glVertex3fv(vtx_trans);
                                    }
                                    else
                                    {
                                        Gl.glVertex3fv(vtx_state);
                                    }

                                    break;
                                }
                            case 0x24:
                                /*
                                      VTX_10 - Set Vertex XYZ Coordinates (W)
                                      Parameter 1, Bit 0-9    X-Coordinate (signed, with 6bit fractional part)
                                      Parameter 1, Bit 10-19  Y-Coordinate (signed, with 6bit fractional part)
                                      Parameter 1, Bit 20-29  Z-Coordinate (signed, with 6bit fractional part)
                                      Parameter 1, Bit 30-31  Not used
                                    */
                                {
                                    int xyz, x, y, z;

                                    xyz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    x = (xyz >> 0) & 0x3FF;
                                    if ((x & 0x200) != 0) x |= -1024;
                                    y = (xyz >> 10) & 0x3FF;
                                    if ((y & 0x200) != 0) y |= -1024;
                                    z = (xyz >> 20) & 0x3FF;
                                    if ((z & 0x200) != 0) z |= -1024;

                                    vtx_state[0] = ((float)x) / 64.0f;
                                    vtx_state[1] = ((float)y) / 64.0f;
                                    vtx_state[2] = ((float)z) / 64.0f;

                                    if (stackID != -1)
                                    {
                                        vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                        Gl.glVertex3fv(vtx_trans);
                                    }
                                    else
                                    {
                                        Gl.glVertex3fv(vtx_state);
                                    }
                                    break;
                                }
                            case 0x25:
                                /*
                                      VTX_XY - Set Vertex XY Coordinates (W)
                                      Parameter 1, Bit 0-15   X-Coordinate (signed, with 12bit fractional part)
                                      Parameter 1, Bit 16-31  Y-Coordinate (signed, with 12bit fractional part)
                                    */
                                {
                                    int xy, x, y;

                                    xy = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    x = (xy >> 0) & 0xFFFF;
                                    if ((x & 0x8000) != 0) x |= -65536;
                                    y = (xy >> 16) & 0xFFFF;
                                    if ((y & 0x8000) != 0) y |= -65536;

                                    vtx_state[0] = ((float)x) / SCALE_IV;
                                    vtx_state[1] = ((float)y) / SCALE_IV;

                                    if (stackID != -1)
                                    {
                                        vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                        Gl.glVertex3fv(vtx_trans);
                                    }
                                    else
                                    {
                                        Gl.glVertex3fv(vtx_state);
                                    }
                                    break;
                                }
                            case 0x26:
                                /*
                                      VTX_XZ - Set Vertex XZ Coordinates (W)
                                      Parameter 1, Bit 0-15   X-Coordinate (signed, with 12bit fractional part)
                                      Parameter 1, Bit 16-31  Z-Coordinate (signed, with 12bit fractional part)
                                    */
                                {
                                    int xz, x, z;

                                    xz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    x = (xz >> 0) & 0xFFFF;
                                    if ((x & 0x8000) != 0) x |= -65536;
                                    z = (xz >> 16) & 0xFFFF;
                                    if ((z & 0x8000) != 0) z |= -65536;

                                    vtx_state[0] = ((float)x) / SCALE_IV;
                                    vtx_state[2] = ((float)z) / SCALE_IV;

                                    if (stackID != -1)
                                    {
                                        vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                        Gl.glVertex3fv(vtx_trans);
                                    }
                                    else
                                    {
                                        Gl.glVertex3fv(vtx_state);
                                    }
                                    break;
                                }
                            case 0x27:
                                /*
                                      VTX_YZ - Set Vertex YZ Coordinates (W)
                                      Parameter 1, Bit 0-15   Y-Coordinate (signed, with 12bit fractional part)
                                      Parameter 1, Bit 16-31  Z-Coordinate (signed, with 12bit fractional part)
                                    */
                                {
                                    int yz, y, z;
                                    yz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    y = (yz >> 0) & 0xFFFF;
                                    if ((y & 0x8000) != 0) y |= -65536;
                                    z = (yz >> 16) & 0xFFFF;
                                    if ((z & 0x8000) != 0) z |= -65536;

                                    vtx_state[1] = ((float)y) / SCALE_IV;
                                    vtx_state[2] = ((float)z) / SCALE_IV;

                                    if (stackID != -1)
                                    {
                                        vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                        Gl.glVertex3fv(vtx_trans);
                                    }
                                    else
                                    {
                                        Gl.glVertex3fv(vtx_state);
                                    }
                                    break;
                                }
                            case 0x28:
                                /*
                                      VTX_DIFF - Set Relative Vertex Coordinates (W)
                                      Parameter 1, Bit 0-9    X-Difference (signed, with 9bit fractional part)
                                      Parameter 1, Bit 10-19  Y-Difference (signed, with 9bit fractional part)
                                      Parameter 1, Bit 20-29  Z-Difference (signed, with 9bit fractional part)
                                      Parameter 1, Bit 30-31  Not used
                                    */
                                {
                                    int xyz, x, y, z;
                                    xyz = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    x = (xyz >> 0) & 0x3FF;
                                    if ((x & 0x200) != 0) x |= -1024;
                                    y = (xyz >> 10) & 0x3FF;
                                    if ((y & 0x200) != 0) y |= -1024;
                                    z = (xyz >> 20) & 0x3FF;
                                    if ((z & 0x200) != 0) z |= -1024;

                                    vtx_state[0] += ((float)x) / SCALE_IV;
                                    vtx_state[1] += ((float)y) / SCALE_IV;
                                    vtx_state[2] += ((float)z) / SCALE_IV;

                                    if (stackID != -1)
                                    {
                                        vtx_trans = CurrentMatrix.MultVector(vtx_state);
                                        Gl.glVertex3fv(vtx_trans);
                                    }
                                    else
                                    {
                                        Gl.glVertex3fv(vtx_state);
                                    }
                                    break;
                                }
                            case 0x40: // Start of Vertex List (W)
                                {
                                    mode = Utils.Read4BytesAsInt32(polydata, commandptr);
                                    commandptr += 4;

                                    switch (mode)
                                    {
                                        case 0:
                                            mode = Gl.GL_TRIANGLES;
                                            break;
                                        case 1:
                                            mode = Gl.GL_QUADS;
                                            break;
                                        case 2:
                                            mode = Gl.GL_TRIANGLE_STRIP;
                                            break;
                                        case 3:
                                            mode = Gl.GL_QUAD_STRIP;
                                            break;
                                        default:
                                            //return ;// FALSE;//throw new Exception();
                                            break;
                                    }

                                    Gl.glBegin(mode);
                                    break;
                                }
                            case 0x41: // End of Vertex List (W)
                                Gl.glEnd();

                                // for vertex mode, display at maximum certain number of vertex-list
                                // decrease cur_vertex so that when we reach 0, stop rendering any further
                                cur_vertex--;
                                if (cur_vertex < 0 && gOptVertexMode)
                                    return; //TRUE;
                                break;
                            default:
                                break;
                            //return FALSE;
                        }
                    }
                }
            }
            string Basic_magic = "HBDF";
            string Model_magic = "MDLF";
            string Tex_magic = "TEXS";
            int file_length1 = 0;
            int file_length2 = 0;
            List<int> mesh_length;
            public void load(byte[] file)
            {
                EndianBinaryReader er = new EndianBinaryReader(new MemoryStream(file), Endianness.LittleEndian);
                if (er.ReadString(Encoding.ASCII, 4) != Basic_magic)
                {
                    MessageBox.Show("File isn't a HBDF file or it isn't decompressed.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                else
                {
                    file_length1 = er.ReadInt32();
                    if (er.ReadString(Encoding.ASCII, 4) != Model_magic)
                    {
                        MessageBox.Show("File is a HBDF file but no MDLF.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                    else
                    {
                        file_length2 = er.ReadInt32();
                        while (er.ReadString(Encoding.ASCII, 4) != "MESH") ;
                        er.BaseStream.Position -= 4;
                        er.ReadString(Encoding.ASCII, 4);
                        mesh_length.Add(er.ReadInt32());
                        /*bool find = false;
                        int g;
                        for (g = 0; g < mesh_length[mesh_length.Count - 1]; g++)
                        {
                            g++;
                            byte[] byt = new byte[2];
                            byt = er.ReadBytes(2);
                            if (byt[0] == 0x40 && byt[1] == 0x22)
                            {
                                find = true;
                                er.BaseStream.Position -= 2;
                                break;
                            }
                        }
                        if (find)
                        {
                            Process3DCommand(er.ReadBytes(mesh_length[mesh_length.Count - 1] - g));
                        }*/
                        er.ReadBytes(0x28);
                        /*else
                        {
                            
                        }*/
                        Process3DCommand(er.ReadBytes(mesh_length[mesh_length.Count - 1] - 0x28));
                    }
                }
            }
        }
    }
}
